00.Groovy

Groovy 语法

Groovy 基本语法

  1. 在 Groovy 中导入语句
    import 语句来导入
import groovy.xml.MarkupBuilder
def xml = new MarkupBuilder()

默认情况下,Groovy 在代码中导入了以下库,这些库不需要显示地导入它们:

import java.lang.*
import java.util.*
import java.io.*
import java.net.*

import groovy.lang.*
import groovy.util.*

import java.math.BigInteger
import java.math.BigDecimal
  1. Groovy 令牌 (Token)
    令牌可以是一个关键字、一个标识符、常量、字符串文字或符号
println("Hello World")

上面的代码中有,有两个令牌:关键字 println 和字符串 Hello World。

  1. Groovy 注释
    单行注释 //
    多行注释 /* */
    文档注释 /** */
  2. 分号
    像 Java 语言一样,需要分号在 Groovy 定义的多个语句之间进行区分。
  3. 标识符 (Identifiers)
    标识符被用来定义变量、函数或其他用户定义的变量。
def employeeName
def student
def stu_name
  1. 关键字 (Keywords)
    关键字作为名称建议是在 Groovy 编程语言中保留的特殊字。 下表列出了在 Groovy 中定义的关键字。
    300n8
  2. 空白 (Whitespaces)
    空白是在编程语言如 Java 和 Groovy 用来形容空格,制表符,换行符和注释术语。空格分隔从另一个声明的一部分,使编译器,其中一个元素标识的声明。
    例如,在下面的代码示例,存在关键字 def 和变量 x 之间的空白。这是为了让编译器知道 DEF 是需要被使用,并且是 x 应该是需要被定义的变量名的关键字。
def x = 5;
  1. 文字 (Literals)
    文字是在 groovy 中表示固定值的符号。Groovy 语言有符号整数,浮点数,字符和字符串。下面是一些在 Groovy 编程语言文字的例子:
12
1.45
‘a’
“aa”
  1. Groovy 变量
    Groovy 中的变量可以通过两种方式定义:使用数据类型的本地语法,或者使用 def 关键字。
// 明确提供类型
int x = 5;

用def关键字
def _Name = "Joe";

//强类型定义
//int x=1
//double y=3.14
//char ch='a'
//boolean flag=true;

//弱类型定义
def x=1  //如果没有初始值,是不能直接使用的,会报NullPointerException
def y=3.14
def ch='a'
def flag=true

//println x.class
//println y.class
//println ch.class
//println flag.class

//弱类型是可以自动转型的
x='jett'
y="jett"
ch='''jett'''
println x.class
println y.class
println ch.class

Groovy 运算符

运算符是一个符号,通知编译器执行特定的数学或逻辑操作。
Groovy 中有以下类型的运算符:

算法运算符

Groovy 语言支持正常的算术运算符任何语言。以下是在 Groovy 中可用的算术运算符:

运算符 描述 例子
+ 两个操作数的加法 1 + 2 将得到 3
- 第一第二操作数相减 2 - 1 将得到 1
* 两个操作数的乘法 2 * 2 将得到 4
/ 两个操作数的除法 3/2 将得到 1.5
% 取模运算 3%2 将得到 1
++ 自增运算,在自身值的基础上加 1 INT X = 5;X ++;X 将得到 6
-- 自减运算,在自身值的基础上减 1 INT X = 5;X - -;X 将得到 4

关系运算符

关系运算符允许对象的比较。以下是在 Groovy 中可用的关系运算符:

检查是否左边的对象比右边的对象大

逻辑运算符

逻辑运算符用于计算布尔表达式:

位运算符

Groovy 中提供了四个位运算符。以下是在 Groovy 中可用的位运算符:

赋值运算符

Groovy 语言也提供了赋值操作符。以下是在 Groovy 提供的赋值运算符:

范围运算符

Groovy 支持范围的概念,并在..符号的帮助下提供范围运算符的符号:

def range = 0..5

这只是定义了一个简单的整数范围,存储到一个局部变量称为范围内的下限为 0 和上限为 5。

运算符优先级

Groovy 数据类型

内置数据类型

byte 用来表示字节值
short 用来表示一个短整型
int 用来表示整数
long 用来表示一个长整型
float 用来表示32位浮点数
double 用来表示64位浮点数
char 定义了单个字符文字
Boolean 表示一个布尔值,可以是true和false
String 以字符串的形式表示的文本

数字类

类型除了基本类型,还允许以下对象类型(有时称为包装器类型)

java.lang.Byte
java.lang.Short
java.lang.Integer
java.lang.Long
java.lang.Float
java.lang.Double

此外,以下类可用于支持高精度计算:

java.math.BigInteger 不可变的任意精度的有符号整数数字
Java.math.BigDecimal 不可变的任意精度的有符号十进制数

Groovy 循环

循环 while/for/for-in

int count = 0
while (count < 5) {
    println(count)
    count++
}
for (int i = 0; i < 5; i++) {
    print(" " + i)
}
int[] arr = [1, 2, 3, 4]
for (int i in arr) {
    print(i + " ")
}

循环控制语句 break/continue

Groovy 条件语句

条件声明需要程序指定一个或者多个条件进行判断,如果条件被确定为真,则要执行一个或多个语句;如果条件被确定为假,则要执行其他语句。

int a = 97
if (a > 100) {
    println("大于100")
} else if (a > 90) {
    println("大于90")
} else {
    println("小于90")
}

Groovy 方法

Groovy 中的方法是使用返回类型或用 def 关键字定义的。方法可以接收任意的参数。定义参数时,不必显示定义类型。可以添加修饰符(public,private,protected)。默认情况下,如果没提供修饰符,默认为 public。

方法参数

static def sum(int a, int b) {
    return a + b
}

默认参数

Groovy 中还有一个规定来指定方法中的参数的默认值。 如果没有值传递给参数的方法,则使用缺省值。 如果使用非默认和默认参数,则必须注意,默认参数应在参数列表的末尾定义。

def sum1(int a, int b = 5) {
    return a + b
}

方法返回值

方法也可以将值返回到调用程序。 这在现在编程语言中是必需的,其中方法执行某种计算,然后将所需值返回到调用方法。

其他同 Java 语言特性

实例方法 (setter/getter)、本地和全局参数、方法属性 (this)、

Groovy 文件 I/O

Groovy 在使用 I/O 时提供了许多辅助方法。 Groovy 提供了更简单的类来为文件提供以下功能:

  1. 读取文件
  2. 写入文件
  3. 遍历文件树
  4. 读取和写入数据对象到文件

始终可以使用下面列出的用于文件 I / O 操作的标准 Java 类:

  1. java.io.File
  2. java.io.InputStream
  3. java.io.OutputStream
  4. java.io.Reader
  5. java.io.Writer

1、读取文件

以下示例将输出 Groovy 中的文本文件的所有行。方法 eachLine 内置在 Groovy 中的 File 类中,目的是确保文本文件的每一行都被读取。

static void test1() {
    def name = "C:/Users/zengfansheng/Desktop/example.txt"
    File file = new File(name)
    file.eachLine {
        line -> println("line:$line")
    }
}

File 类用于实例化以文件名作为参数的新对象。 然后它接受 eachLine 的函数,将它放到一个 line 的变量并相应地打印它。

2、读取文件的内容到字符串

如果要将文件的整个内容作为字符串获取,可以使用文件类的 text 属性:

File file = new File("C:/Users/zengfansheng/Desktop/example.txt")
println file.text

3、写入文件

如果你想写入文件,你需要使用作家类输出文本到一个文件中。

File file = new File("C:/Users/zengfansheng/Desktop/", "example2.txt")
file.withWriter("utf-8") {
    writer -> writer.writeLine "hello world write file."
}

4、获取文件的大小

如果要获取文件的大小,可以使用文件类的 length 属性来获取文件的大小。

File file = new File("C:/Users/zengfansheng/Desktop/", "example2.txt")
println("The file ${file.absolutePath} has ${file.length()} bytes")

5、测试文件是否是目录

File file = new File("C:/Users/zengfansheng/Desktop/", "example2.txt")
println "File? ${file.isFile()}"
println "Directory? ${file.isDirectory()}"

6、创建目录

如果要创建一个新目录,可以使用 File 类的 mkdir 函数。

File file = new File("C:/Users/zengfansheng/Desktop/test11")
file.mkdirs() // file.mkdir()

7、删除文件

file.delete()

8、复制文件

File src = new File("C:/Users/zengfansheng/Desktop/", "src.txt")
File dst = new File("C:/Users/zengfansheng/Desktop/", "dst.txt")
dst << src.text

将创建文件 dst.txt,并将文件 src.txt 的所有内容复制到此文件。

9、获取目录内容

Groovy 还提供了列出驱动器中的驱动器和文件的功能。
以下示例显示如何使用 File 类的 listRoots() 函数显示机器上的所有驱动器:

def rootFiles = new File("C:/Users/zengfansheng/Desktop/hexo")
File[] files = rootFiles.listRoots()
files.each {
    file -> println file.absolutePath
}

以下示例显示如何使用 File 类的 eachFile() 函数列出特定目录中的文件(仅列出目录的子文目录和文件):

def rootFiles = new File("C:/Users/zengfansheng/Desktop/andfix")
rootFiles.eachFile {
    file -> println file.absolutePath
}

如果要递归显示目录及其子目录中的所有文件,则可以使用 File 类的 eachFileRecurse() 函数。以下示例显示如何完成此操作。

def rootFiles = new File("C:/Users/zengfansheng/Desktop/andfix")
rootFiles.eachFileRecurse {
    file -> println file.absolutePath
}

Groovy 字符串

Groovy 提供了多种表示 String 字面量的方法。 Groovy 中的字符串可以用 单引号(')双引号(")三引号("') 括起来。此外,由三重引号括起来的 Groovy 字符串可以跨越多行。

String a = 'this is a string'
String b = "this is b string"
String c = "' fajlfdjdlkasjfdljc" +
        "fjaldjf '"

字符串索引

Groovy 中的字符串是字符的有序序列。字符串中的单个字符可以通过其位置访问。这由索引位置给出。
字符串索引从零开始,以小于字符串长度的一个结束。 Groovy 还允许负索引从字符串的末尾开始计数。

String a = 'this is a string'
println(a[3]) // s  3表示第4个字符
println(a[-2]) // n , -2表示倒数第2个字符

字符串操作

1、+ 字符串连接

字符串连接可以通过简单的 + 运算符来完成。

String a = "Hello"
String b = "World"
println(a+b)

2、* 字符串重复

字符串的重复可以通过简单的 * 运算符完成。

String*number
String a = "Hello"
println(a*3)

3、length() 字符串的长度

字符串方法

除了 Java 中 String 的方法外,还有:

String hello = "Hello World!"
hello.eachMatch(".") {
    ch -> println(ch)
}
String hello = "Hello World!"
println(hello.minus("Hell")) // o World!
println(hello.plus("hacket")) // Hello World!hacket
String hello = "Hello World"
println(hello.next()) // Hello Worle
println(hello.previous()) // Hello Worlc
String hello = "Hello World"
println(hello.padLeft(hello.length() + 4, "+++")) // ++++Hello World
println(hello.padRight(hello.length() + 1, "-")) // Hello World-
String hello = "Hello World"
println(hello.reverse()) // dlroW olleH
//单引号   和java中是一样的
def name='jett'
//双引号
def name2="Hello:${name}"
//三引号   原始格式
def name3='''hello
jett "'''


//println name
//println name2
//println name3
//println name.class
//println name2.class
//println name3.class

//输入表达式
//def sum="${3+2}${name}"
//println sum
//
//String echo(String msg){
//    println msg
//}
//echo(sum)


def string='hello'
def string2='el'
//groovy中常用的string相关的API
println string>string2
println string[1..2]
//减法
println string.minus(string2)
//逆序
println string.reverse()
//首字母大写
println string.capitalize()
//字符串中是否有数字字符
println string.isEmpty()

Groovy 范围 (ranges)

Range 是指定范围值的序列。Range 由序列中的第一个和最后一个值表示,Range 可以是 inclusive 或 exclusive。inclusive Range 包括从第一个到最后一个的所有值,exclusive Range 包括除最后一个之外的所有值。

Range 方法

static void test10() {
    def v1 = 1..10
    def v2 = 1..<10
    def v3 = 'a'..'x'
    def v4 = 10..1
    def v5 = 'x'..'a'
    println(v1)
    println(v2)
    println(v3)
    println(v4)
    println(v5)

    println("================================")

    println(v1.contains(8))
    println(v1.contains(-2))

    println("================================")

    println(v2.get(0))
//        println(v2.get(11))

    println("================================")
    println(v1.getFrom())
    println(v4.getFrom())
    println(v1.getTo())
    println(v4.getTo())
    println("================================")
    println(v1.isReverse())
    println(v4.isReverse())

    println("================================")

    println(v1.size())
    println(v2.size())

    println("================================")

    println(v1.subList(1, 4))

}

运行结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[x, w, v, u, t, s, r, q, p, o, n, m, l, k, j, i, h, g, f, e, d, c, b, a]
================================
true
false
================================
1
================================
1
1
10
10
================================
false
true
================================
10
9
================================
[2, 3, 4]

Groovy 列表 (Lists)

列表是用于存储数据项集合的结构。在 Groovy 中,List 保存了一系列对象引用。List 中的对象引用占据序列中的位置,并通过整数索引来区分。列表文字表示为一系列用逗号分隔并用方括号括起来的对象。
要处理列表中的数据,我们必须能够访问各个元素。 Groovy 列表使用索引操作符 [] 索引。列表索引从零开始,这指的是第一个元素。以下是一些列表的示例:

List 中的方法

////定义list
////def list=new ArrayList()
//def list=[1,2,3,4,5]
//println list.class
//println list.size()
////定义数组
//def array=[1,2,3,4,5] as int[]
//
////1.添加
//list.add(6)
//list<<2
//println list
//def plusList=list+5
////println plusList
//plusList.add(3,9)
////println plusList

//2.删除
//list.remove(2)  //删除下标位置的对象
//list.remove((Object)2)
//list.removeElement(2)
//list.removeAll{
//    return it%2!=0
//}
//println list-[2,4]


////3.查找
//def findList=[5,-2,1,4,3]
////查找满足条件的第一个数据
//int result=findList.find{
//    return it%2 == 0
//}
//println result
////查找所有满足条件的数据
//def result2=findList.findAll({
//    return it%2 !=0
//})
//println result2
//
////查找是否有满足条件的数据
//def result3=findList.any{
//    return it%2 ==0
//}
//println result3
//
////查找是否全部满足条件
//def result4=findList.every{
//    return it%2 ==0
//}
//println result4
//
////查找最大值与最小值
//def result5=findList.min{
//    return Math.abs(it)
//}
//println result5
//def result6=findList.max{
//    return Math.abs(it)
//}
//println result6
////统计
//int result7=findList.count{
//    return it>0
//}
//println result7


//4.排序
def sortList=[5,-2,1,4,3]
sortList.sort({a,b ->
    a == b ? 0 : Math.abs(a)>Math.abs(b) ? 1 :-1
})
println sortList

//对象排序
def sortStringList=['aaaaa','bbbb','c','ddd','ee']
sortStringList.sort({it ->
     return it.size()
})
println sortStringList

Groovy 映射 (Maps)

映射 (Map,也称为关联数组,字典,表和散列),是对象引用的无序集合。Map 集合中的元素由键值访问。Map 中使用的键可以是任意类。当我们插入到 Map 集合中时,需要两个值:键和值。
以下是一些映射的例子:

Map 中的方法

案例 1:

private static void testMap() {
    def map = ["topicName1": "list", 'topicName': 'map', null: "nullValue"]
    println(map) // [topicName1:list, topicName:map, null:nullValue]

    println(map.containsKey("topicName")) // true
    println(map.containsKey("null")) // true
    println(map.containsValue("map")) // true

    println(map.put("key1", "value2")) // null
    println(map.put("key1", "value1")) // value2
    println(map) // [topicName1:list, topicName:map, null:nullValue, key1:value1]

    println(map.size()) // 4

    println(map.values()) // [list, map, nullValue, value1]
}
private static void testMap() {
    def map = ["topicName1": "list", 'topicName': 'map', null: "nullValue"]
    println(map) // [topicName1:list, topicName:map, null:nullValue]

    println(map.containsKey("topicName")) // true
    println(map.containsKey("null")) // true
    println(map.containsValue("map")) // true

    println(map.put("key1", "value2")) // null
    println(map.put("key1", "value1")) // value2
    println(map) // [topicName1:list, topicName:map, null:nullValue, key1:value1]

    println(map.size()) // 4

    println(map.values()) // [list, map, nullValue, value1]
}

案例 2:

////定义与读取
//def colors=[red:'ff0000',green:'00ff00',blue:'0000ff']
//println colors['red']
//println colors.red
////如果使用colors.class  会把class当成一个键
////class java.util.LinkedHashMap
//println colors.getClass()
//
////添加普通对象
//colors.yellow='ffff00'
//println colors
////添加集合对象
//colors.map = [key1:1,key2:2]
//println colors.toMapString()

//遍历map
def teachers = [
        1: [number: '001', name: 'jett'],
        4: [number: '004', name: 'alven'],
        3: [number: '003', name: 'lance'],
        2: [number: '002', name: 'leo'],
        6: [number: '006', name: 'allen'],
        5: [number: '005', name: 'zero'],
        7: [number: '007', name: 'derry'],
        8: [number: '008', name: 'jett']
]

//用键值对的方式
//teachers.each {def key,def value ->
//    println "key=${key}---value=${value}"
//}
//用entry对象的方式
//teachers.each {def teacher ->
//    println "key=${teacher.key}---value=${teacher.value}"
//}
//带索引的方式
//teachers.eachWithIndex{ def teacher,int index->
//    println "index=${index}---key=${teacher.key}---value=${teacher.value}"
//}
//teachers.eachWithIndex{ def key,def value,int index->
//    println "index=${index}---key=${key}---value=${value}"
//}

//map的查找
//def entry=teachers.find{def teacher ->
//    return teacher.value.name=='jett'
//}
//println entry

//def entry=teachers.findAll{def teacher ->
//    return teacher.value.name=='jett'
//}
//println entry

//def count=teachers.count{def teacher ->
//    return teacher.value.name=='jett'
//}
//println count

//实现嵌套查询
def number=teachers.findAll{def teacher->
    return teacher.value.name=='jett'
}.collect(){
    return it.value.number
}
println number.toListString()

//实现分组查询
def group=teachers.groupBy {def teacher ->
    return teacher.value.name=='jett' ? 'group1' : 'group2'
}
println group.toMapString()

//排序  注意:map会返回一个新的map   list是在原来的list中进行排序
def sort=teachers.sort{def t1,def t2 ->
    return t1.key > t2.key ? 1 : -1
}
println sort.toMapString()

Groovy 日期和时间

类 Date 表示特定的时刻,具有毫秒精度。 Date 类有两个构造函数

Date 中方法

Groovy 正则表达式

正则表达式用于在文本中查找子字符串的模式。Groovy 使用 ~regex 表达式本地支持正则表达式。regex 包含的文本表示用于比较的表达式。

当 Groovy 运算符 =~ 在 if 或 while 语句中作为判断表达式时,左侧的 String 操作数与右侧的正则表达式操作数匹配,判断条件为 true

if ("hacketfafdf" =~ "hacket") {
    println("true") // true
}

正则表达式规则,可以使用的特殊字符

  1. 有两个特殊的位置字符用于表示一行的开始和结束:^$
  2. 正则表达式也可以包括量词:+ 表示一次或多次,应用于表达式的前一个元素;* 表示零个或多个出现;? 表示零或一次
  3. 元字符 {} 用于匹配前一个字符的特定数量的实例
  4. 在正则表达式中,. 可以表示任何字符。(通配符)
  5. 正则表达式可以包括字符类。一组字符可以作为简单的字符序列,包含在元字符 [] 中,如:[aeiou]。对于字母或数字范围,可以使用 [a-z][a-mA-M] 中的 - 分隔符。字符类的补码由方括号内的前导插入符号表示,如 [^a-z],并表示除指定的字符以外的所有字符。

Groovy 异常处理

同 Java 异常处理机制

Groovy 面向对象

同 Java 面向对象

class/接口
抽象
封装
继承
多态
trait

接口

  1. 接口中不能定义非 public 方法的
/**
 * 接口中不能定义非public方法的
 */
interface Action {
    void eat()
    void drink()
    void play()
}

package objectorention

// 1.在groovy中所有的类型   默认都是public
// 2.所有的类都是继承自GroovyObject
class Person implements DefaultAction{
    String name
    Integer age
    def increateseAge(Integer year){
        this.age+=year
    }

    @Override
    void eat() {

    }
}

创建对象和使用对象:

package objectorention

def person=new Person(name:'jett',age:18)
println "name="+person.name+"  age="+person.age
println "name="+person.getName()+"  age="+person.getAge()

person.increateseAge(10)
println "name="+person.name+"  age="+person.age

person.play()

trait

package objectorention

trait DefaultAction {
    abstract void eat()
    void play(){
        println 'I can play!'
    }
}

Groovy 泛型

同 Java 泛型

Groovy 之 XML

XML Markup Builder

MarkupBuilder 用于构造整个 XML 文档。通过首先创建 XML 文档类的对象来创建 XML 文档。一旦创建了对象,可以调用伪方法来创建 XML 文档的各种元素。

def markBuilder = new MarkupBuilder()
markBuilder.collection(shelf: 'New Arrivals') {
    movie(title: 'Enemy Behind')
    type('War, Thriller')
    format('DVD')
    year('2003')
    rating('PG')
    stars(10)
    description('Talk about a US-Japan war')
}

结果:

<collection shelf='New Arrivals'>
  <movie title='Enemy Behind' />
  <type>War, Thriller</type>
  <format>DVD</format>
  <year>2003</year>
  <rating>PG</rating>
  <stars>10</stars>
  <description>Talk about a US-Japan war</description>
</collection>

XmlParser

XmlParser 来解析 XML 文档

案例:

import groovy.xml.MarkupBuilder
import groovy.xml.XmlSlurper

final String xml='''
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.jvm_demo_20200601">
    <test>12345</test>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity2">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
'''

////解析XML数据
//def xmlSluper=new XmlSlurper()
//def result=xmlSluper.parseText(xml)
//println result.@package
//println result.test.text()
////读取有域名空间的节点
//result.declareNamespace('android':'http://schemas.android.com/apk/res/android')
//println result.application.@'android:allowBackup'
//println result.application.activity[0].@'android:name'
//println result.application.activity[1].@'android:name'
//
////遍历XML节点
//result.application.activity.each{activity ->
//    println activity.@'android:name'
//}

/**
 * 生成XML格式数据
 * <html>
 *     <title id='123',name='android'>xml生成
 *          <person></person>
 *     </title>
 *     <body name='java'>
 *         <activity id='001' class='MainActivity'>abc</activity>
 *         <activity id='002' class='SecActivity'>abc</activity>
 *     </body>
 * </html>
 */
def sw=new StringWriter()
def xmlBuilder=new MarkupBuilder(sw)
xmlBuilder.html(){
    title(id:'123',name:'android','xml生成'){
        person()
    }
    body(name:'java'){
        activity(id:'001',class:'MainActivity','abc')
        activity(id:'002',class:'SecActivity','abc')
    }
}
println sw

Groovy 之 JSON

用 Groovy 来解析和生成 JSON 对象

API 功能
JsonSlurper JsonSlurper 是一个将 JSON 文本或阅读器内容(reader content)解析为 Groovy 中的数据结构(如 Map、List、Integer、Double、Boolean 和 String)
JsonOutput 代表了将 Groovy 对象序列化成 JSON 字符串
import groovy.json.JsonOutput
import groovy.json.JsonSlurper

// 对象转成json字符串
def list=[new Person(name:'jett',age:18),
            new Person(name:'lance',age:18)]
println JsonOutput.toJson(list)
// 格式化
def json=JsonOutput.toJson(list)
println JsonOutput.prettyPrint(json)

// json字符串转成对象
def jsonSluper=new JsonSlurper()
def object=jsonSluper.parse("[{\"age\":18,\"name\":\"jett\"},{\"age\":18,\"name\":\"lance\"}]".getBytes())
println object

def object2=jsonSluper.parse("[{\"abc\":\"jett\"}]".getBytes())
println object2.abc

class Person {
    String name
    Integer age
}

生成 json

static def json(List<String> atUsers) {
    def json = new JsonBuilder()
    json {
        msgtype "markdown"
        markdown {
            title "title"
            text "text"
        }
        at {
            if (!atUsers.isEmpty()) {
                atMobiles(atUsers)
            }
            isAtAll false
        }
    }
    return json.toString()
}

输出:

{"msgtype":"markdown","markdown":{"title":"title","text":"text"},"at":{"atMobiles":["134","135"],"isAtAll":false}}

Groovy 注解 (Annotations)

用@interface 表示,类似 Java。

@interface Simple { // 不能嵌套在class内部
    int status() // default 0
}
@Simple(status = 1)
static class User {
    String username
    int age
}

元注解(Meta Annotations)

Groovy 闭包 (Closure)

见下面的 Groovy语法之闭包.md

Groovy 对 Java 的优化

  1. 表达式后面的分号是可选的
  2. 每个类、构造器和方法默认是 public 的
  3. groovy 方法体中的最后一个表达式的值会被作为返回值
  4. groovy 编译器自动加上 getter/setter 方法
  5. 类的属性可以通过点 (.) 来获取,底层是 groovy 自动调用 getter/setter 方法
  6. 用 == 比较两个类的实例,底层调用的是 equals() 方法,这个操作也避免了 npe 异常

可选的括号

Groovy 中如果方法签名需要至少一个参数的话,则方法调用可以省略括号。

initProjectVersion(1,2)
initProjectVersion 1,2

字符串

// 1、单引号
def myStr1 = `This is a single-quoted String`
// 2、双引号,也叫GString。可${str}来表示
def myStr2 = "This is a double-quoted String"
// 3、三引号
def myStr3 = """
  This is
  a multiline String
"""

命名参数

一个类没有默认构造器,Groovy 会调用类的默认构造器,然后为每个参数调用对应的 setter 方法

ProjectVersion pv = new ProjectVersion(major:1,minor:10)

闭包

闭包代码在委托的闭包上执行。默认的,这个委托就是闭包的所有者(你在 Groovy 脚本中定义了一个闭包,那么这个闭包的所有者就是 groogy.lang.Script 实例)。闭包的隐式变量 delegate 允许你重新定义默认的所有者。

Groovy 新的特性

trait

trait 是语言的结构构造,它允许:

  1. 行为的组成
  2. 接口的运行时实现
  3. 和静态类型检查/编译相兼容

它们可以被看作是承载默认实现和状态的接口,用 trait 关键字定义。

trait Marks {
    void displayMarks() {
        println("display Marks")
    }
}

然后可以用 implement 关键字类似接口的方式实现 trait

trait Marks {
    void displayMarks() {
        println("display Marks")
    }
}
static class StuMarks implements Marks {
    int studentID
    int marks1;
}

trait 可以实现接口

trait 可以实现接口,在这种情况下,使用 implements 关键字来实现声明的接口。

interface Total {
    void displayTotal()
}
trait Marks implements Total {
    void displayMarks() {
        println("display Marks")
    }
    @Override
    void displayTotal() {
        println("display total")
    }
}
class StuMarks implements Marks {
    int studentID
    int marks1
}

trait 可以定义属性

trait 可以定义属性:

trait Marks {
    int Marks1
    void displayMarks() {
        this.Marks1 = 10
        println("display Marks:" + Marks1)
    }
}

trait 行为组合(Composition of Behaviors)

类似接口一样,可以被同一个 class 实现多个 trait,然后该 class 就具备了这两个 trait 的行为。

trait Marks {
   void DisplayMarks() {
      println("Marks1");
   }
}
trait Total {
   void DisplayTotal() {
      println("Total");
   }
}
class Student implements Marks,Total {
   int StudentID
}

继承 trait(Extending Traits)

trait 可以继承另外一个 trait,使用 extends 关键字。

class Example {
   static void main(String[] args) {
      Student st = new Student();
      st.StudentID = 1;
      println(st.DisplayMarks());
   }
}
trait Marks {
   void DisplayMarks() {
      println("Marks1");
   }
}
trait Total extends Marks {
   void DisplayMarks() {
      println("Total");
   }
}
class Student implements Total {
   int StudentID
}

Groovy 闭包

闭包的定义

闭包是 Groovy 中非常重要的一个数据类型或者说一种概念。闭包是一种数据类型,它代表了一段可执行的代码。闭包的语法定义:

{ [closureParameters -> ] statements }
  1. 其中 [closureParameters->] 部分是一个可选的以逗号分隔的参数列表
  2. statements 可以为空,一行或者多行代码

当参数列表确定时,-> 是必须的,他负责把参数和闭包的代码块分开,代码块可以由一行或者多行语句组成

一些闭包常用的定义形式:

{ item++ }                                          

{ -> item++ }                                       

{ println it }                                      

{ it -> println it }                                

{ name -> println name }                            

{ String x, int y ->                                
    println "hey ${x} the value is ${y}"
}

{ reader ->                                         
    def line = reader.readLine()
    line.trim()
}

如果闭包没有定义参数的话,则隐含有一个参数,这个参数的名字交 it,和 this 作用类似,it 代表闭包的参数。

闭包作为一个对象

闭包实质上是一个 groovy.lang.Closure 类的实例,虽然他是一个代码块,但是他可以为任何一个变量或者字段赋值,闭包中可以包含代码的逻辑,闭包中的最后一行语句,表示该闭包的返回值,不论该语句是否有 return 关键字

// 可以将闭包分配给一个变量,这个变量就是groovy.lang.Closure的一个实例
def listener = { e -> println "Clicked on $e.source" }      
assert listener instanceof Closure

// 如果不使用def,可以将一个闭包分配给一个类型为groovy.lang.Closure的变量
Closure callback = { println 'Done!' }     

// 可以通过使用groovy.lang.Closure的泛型来指定闭包的返回类型
Closure<Boolean> isTextFile = {
    File it -> it.name.endsWith('.txt')                     
}

调用闭包

下面是一段简短的 closure:

void test1() {
    def clos = { println "i am a closure!" }
    clos.call()
    // clos()  // 这个也可以调用
}

上面 { println "i am a closure!"} 称之为闭包。这段代码块(闭包)可以通过 闭包.call()闭包() 或来执行。

  1. 闭包作为一个匿名的代码块,可以像方法那样被调用
  2. 闭包内的代码只有在闭包被调用时才会执行,调用可以像常规方法那样来完成
  3. 可以显式的调用 call 方法调用
  4. 和方法不一样的是,闭包始终有一个返回值

闭包的参数 (Formal parameters in closures)

闭包的参数遵循与常规方法的参数相同的原则 (参数用 , 分隔):

  1. 可选的类型
  2. 名字
  3. 可选的默认值

闭包的显式参数

闭包也可以包含形式参数,以使它们更有用,就像 Groovy 中的方法一样:

void test1() {
    def clos = { param -> println "i am a closure!${param}" }
    clos.call("hacket")
}

上面的代码用 $param${param} 是 closure 接收一个参数,当通过 call 调用闭包时,可以传递一个参数给闭包。
上面的代码也等价于:

void test1() {
    def clos = { println "i am a closure!$it" }
    clos.call("hacket")
}

示例:

// 只有1个参数,只有参数名,没有类型
def closureWithOneArg = { str -> str.toUpperCase() }
assert closureWithOneArg('groovy') == 'GROOVY'

// 只有1个参数,有参数类型和参数名字
def closureWithOneArgAndExplicitType = { String str -> str.toUpperCase() }
assert closureWithOneArgAndExplicitType('groovy') == 'GROOVY'

// 有2个参数,只有参数名,没有类型
def closureWithTwoArgs = { a,b -> a+b }
assert closureWithTwoArgs(1,2) == 3

// 有2个参数,有参数名和参数类型
def closureWithTwoArgsAndExplicitTypes = { int a, int b -> a+b }
assert closureWithTwoArgsAndExplicitTypes(1,2) == 3

// 有2个参数,第1个参数只有名字,第2个参数有参数类型和名字
def closureWithTwoArgsAndOptionalTypes = { a, int b -> a+b }
assert closureWithTwoArgsAndOptionalTypes(1,2) == 3

// 有2个参数,第1个参数有参数类型和参数名字,第2个参数有参数类型、参数名字和参数默认值
def closureWithTwoArgAndDefaultValue = { int a, int b=2 -> a+b }
assert closureWithTwoArgAndDefaultValue(1) == 3

闭包的隐式参数

当一个闭包没有明确定义一个参数列表(使用 ->)时,闭包总是定义一个隐式参数,并将其命名为 it。

def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
// 等同于
def greeting = { it -> "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'

如果想声明一个不接受参数的闭包,并且必须限制为没有参数的调用,那么必须声明一个明确的空参数列表:

def magicNumber = { -> 42 }

// this call will fail because the closure doesn't accept any argument
magicNumber(11)
  1. 当闭包作为闭包或方法的最后一个参数,可以将闭包从参数圆括号中提取出来接在最后,如果闭包是唯一的一个参数,则闭包或方法参数所在的圆括号也可以省略
  2. 对于有多个闭包参数的,只要是在参数声明最后的,均可以按上述方式省略
def runTwice = { a, c -> c(c(a)) }  
assert runTwice( 5, {it * 3} ) == 45 //usual syntax  
assert runTwice( 5 ){it * 3} == 45  
    //when closure is last param, can put it after the param list  
  
def runTwiceAndConcat = { c -> c() + c() }  
assert runTwiceAndConcat( { 'plate' } ) == 'plateplate' //usual syntax  
assert runTwiceAndConcat(){ 'bowl' } == 'bowlbowl' //shortcut form  
assert runTwiceAndConcat{ 'mug' } == 'mugmug'  
    //can skip parens altogether if closure is only param  
  
def runTwoClosures = { a, c1, c2 -> c1(c2(a)) }  
    //when more than one closure as last params  
assert runTwoClosures( 5, {it*3}, {it*4} ) == 60 //usual syntax  
assert runTwoClosures( 5 ){it*3}{it*4} == 60 //shortcut form

默认参数

// 闭包支持默认参数
cClosure("hello","clat")   // hello, clat!
def dClosure = {
    name,address = "shenzhen" ->
    println "${name},${address}!"
}
dClosure("hacket"); // hacket,shenzhen!

闭包和变量 (Closures and Variables)

闭包代码块中可以引用外部定义的变量:

def str1 = "Hello"
def clos = { param -> println "${str1} $param" }
clos.call(" World!") // Hello  World!

str1 = "Welcome"
clos(" World!") // Welcome  World!

带返回值的闭包

//定义与使用
//无参数的闭包
//def closure={
//    println "hello groovy!"
//}
//closure()
//closure.call()

//带参数的闭包
//def closure={String name,int age->
//    println "hello ${name}:age ${age}"
//}
//closure.call("jett",18)

//带默认参数
//def closure={
//    println "hello ${it}"
//}
//closure.call("jett")

//闭包的返回值
def closure={
    println "hello ${it}"
    return "123"
}
def result=closure.call("jett")
println "result="+result

可变参数

闭包可以象任何其他方法一样声明可变参数

def concat1 = { String... args -> args.join('') }           
assert concat1('abc','def') == 'abcdef'                     
def concat2 = { String[] args -> args.join('') }            
assert concat2('abc', 'def') == 'abcdef'

def multiConcat = { int n, String... args ->                
    args.join('')*n
}
assert multiConcat(2, 'abc','def') == 'abcdefabcdef'

应用闭包

在方法中使用闭包

  1. 闭包也可以作为方法的参数。
    在 Groovy 中,很多用于数据类型(如 List 和 Map)的内置方法都有闭包作为参数类型。
static void test3() {
    def str1 = "Hello"
    def clos = { param -> println "$str1 $param" }
    display(clos)
}

def static display(clo) {
    clo.call("Inner")
}
  1. 匿名内联函数,也称为一个闭包
/**
 * 匿名内联函数,也称为一个闭包。
 * 基本类型相关的API
 */
int x=fab(5)
int fab(int number){
    int result=1;
    1.upto(number,{num -> result *= num})
    return result
}
println x;

int fab2(int number){
    int result=1
    number.downto(1){
        num-> result*=num
    }
    return result
}
println fab2(5)

int sum(int number){
    int result=0;
    number.times {
        num-> result+=num;
    }
    return result
}
println sum(5)

和 String 相关的 API

/**
 * 和String相关的API
 */
String str="2 and 3 is 5"
//each遍历
//str.each {
//    String s->print s.multiply(2)
//}

//find查找符合条件的第一个字符
//println str.find{
//    String s->s.isNumber()
//}

//findAll 查找符合条件的所有字符
//def list=str.findAll{
//    String s-> s.isNumber()
//}
//println list.toListString()

//any 查找是否存在符合条件的字符
//def result=str.any{
//    String s->s.isNumber()
//}
//println result

//every查找是否所有字符都符合条件
//def result=str.every{
//    String s->s.isNumber()
//}
//println result

//对str的每一位单独操作后的结果保存到一个集合中
def list=str.collect{
    it.toUpperCase()
}
println list.toListString()

List/Map 中的闭包

static void test4() {
    def list = [11, 23, 12, 14]
    list.each { println it }
    list.each { num ->
        if (num % 2 == 0) {
            println("偶数:" + num)
        }
    }

    def map = ["TopicName": "Maps", "TopicDescription": "Methods in Maps"]
    map.each { println it }
    map.each { println "${it.key} maps to $it.value" }
}
def list2 = [1, 2, 0, 43, 34]
def value = list2.find({ num -> num >= 2 })
println(value) // 2

def val2 = list2.findAll { num -> num >= 2 }
val2.each { println it } // 2 43 34

def any = list2.any { it > 2 }
println(any) // true
def every = list2.every { it > 2 }
println(every) // false

def list3 = list2.collect { it * it }
list3.each { println(it) } // 1 4 0 1849 1156

Groovy Bean 和闭包

// groovy bean
class GroovyBeanExample {
   private String name
}

def bean = new GroovyBeanExample();
bean.name = "my name is hacket!"
println bean.name

// 闭包
// 闭包是一个可执行的代码块。

// 1、闭包
def aClosuer = {
     println "Hello Closure!";
}
println aClosuer; // my$_run_closure1@962257c
// 闭包的调用
aClosuer.call(); // 使用call()调用闭包 Hello Closure!
aClosuer(); // 调用闭包的简写方式,类似于方法调用。 Hello Closure!
// 闭包在调用的时候才会执行

// 2、隐式参数化闭包
def bClosure = {
  println "Hello ${it}" //it 是闭包的单个隐含参数。
}
bClosure.call("clat") // Hello clat
bClosure("clat") // Hello clat
bClosure "clat" // Hello clat

// 3、显示参数
def cClosure = {
    name,address -> 
    println "${name},${address}!"
}
// 闭包支持默认参数
cClosure("hello","clat")   // hello, clat!
def dClosure = {
    name,address = "shenzhen" ->
    println "${name},${address}!"
}
dClosure("hacket"); // hacket,shenzhen!

println('=============================');

// 4、闭包作用域
def name = "hacket..."
def eClosure = {
    println name;
}
eClosure(); // hacket...

def aMethod(Closure closure){ 
    name = "a hacket"; // 这里的name只是方法的局部变量,闭包不能访问
    closure();
}
aMethod(eClosure); // hacket...

def fClosure = {
    name = "f hacket"; // 这里的name是闭包里面的,实就是可执行片段修改了外围的一个变量
    eClosure();
}
fClosure(); // f hacket

// 5、闭包返回值
def gClosure = {
    number  ->
    return number * 2;
}
println gClosure(3); // 6

// 6、闭包与集合、字符串
// 遍历集合
def nameList = ["clat","escaflone","Aldern"]
nameList.each{
    myname ->
    print myname+"_" 
}
println ""
// clat_escaflone_Aldern_

// 遍历map
def nameMap = [1:'clat',2:'escaflone',3:'Aldern']
nameMap.each{
    map ->
    println map.key +":"+ map.value
} 
// 1:clat
// 2:escaflone
// 3:Aldern

// 遍历Range
(1..<10).each{
    print it+"-"
}
// 1-2-3-4-5-6-7-8-9-
println ""

// 遍历String
"hacket".each{
    print it+"~"
}
// h~a~c~k~e~t~
println ""

// 7、闭包嵌套
def outClosure = {
    country ->
    println "country-->"+country;
    
    def innerClosure = {
        city ->
        println "city-->"+city
    }
    
    innerClosure("shenzhen");
}
outClosure("China");
// country-->China
// city-->shenzhen

闭包委托策略(this,owner,deleate)

Groovy 的闭包比 Java 的 Lambda 表达式功能更强大。原因就是 Groovy 闭包可以修改委托对象和委托策略。这样 Groovy 就可以实现非常优美的领域描述语言(DSL)了。Gradle 就是一个鲜明的例子。

Groovy 闭包有三个相关的对象:

  1. this 即闭包定义所在的类
  2. owner 即闭包定义所在的对象或闭包
  3. delegate 即闭包中引用的第三方对象

this

this 代表闭包定义所在的类;在闭包中,调用 getThisObject() 将返回闭包定义的闭包类,和显式的调用 this 一样

class Enclosing {
    def run() {
        def whatIsThisObject = {
            println("Closure.this=${this}") // this=me.hacket.groovy.Enclosing@1603cd68
            getThisObject()
        }
        assert whatIsThisObject() == this // true
        println("Enclosing.this=${this}") // this=me.hacket.groovy.Enclosing@1603cd68

        def whatIsThis = { this }
        assert whatIsThis() == this // true
    }
}

class EnclosedInInnerClass {
    class Inner {
        Closure cl = { this } // 这个this为Inner对象,返回的就是inner对象
    }

    void run() {
        def inner = new Inner()
        assert inner.cl.call() == inner // true
    }
}

owner

owner 代表闭包定义所在的对象或闭包

delegate

可以使用 delegate 属性或者调用 getDelegate 方法来访问闭包中的 delegate,delegate 在 Groovy 中是一个很重要的概念,delegate 需要我们手动指定;默认 delegate 和 owner 一样

class Enclosing {
    void run() {
        def cl = { getDelegate() }                          
        def cl2 = { delegate }                              
        assert cl() == cl2()                                
        assert cl() == this                                 
        def enclosed = {
            { -> delegate }.call()                          
        }
        assert enclosed() == enclosed                       
    }
}

闭包的 delegate 可以被改变成任何对象。 我们通过创建两个不是彼此的子类的类来说明这一点,但都定义了一个名为 name 的属性:

class Person {
    String name
}
class Thing {
    String name
}

def p = new Person(name: 'Norman')
def t = new Thing(name: 'Teapot')

// 然后定义一个闭包通过delegate来调用name属性
def upperCasedName = { delegate.name.toUpperCase() }

// 然后通过更改闭包的委托,可以看到目标对象将会改变
upperCasedName.delegate = p
assert upperCasedName() == 'NORMAN'

upperCasedName.delegate = t
assert upperCasedName() == 'TEAPOT'

Delegation strategy(委托策略)

无论什么时候,在闭包中,访问某个属性时都没有明确地设置接收者对象,那么就会调用一个委托策略

class Person2 {
    String name
}

static void main(String[] args) {
    def p = new Person2(name: 'Igor')
    def cl = { name.toUpperCase() }
    cl.delegate = p

//    println("cl.owner=${cl.owner}, name=${cl.owner.name}") // MissingPropertyException
    println("cl.delegate=${cl.delegate},  name=${cl.delegate.name}") // cl.delegate=me.hacket.groovy.Person2@6ce86ce1,  name=Igor

    // name property be resolved transparently on the delegate object!
    assert cl() == 'IGOR' // true
}

调用 name 属性并没有执行是谁的 name 属性,然后把闭包 cl 的 delegate 设置为 p,这里没有显式的给 delegate 设置一个接收者,但是能成功访问到 name 属性,因为相应的属性解析策略:

  1. Closure.OWNER_FIRST,默认策略,首先从 owner 上寻找属性或方法,找不到则在 delegate 上寻找。
  2. Closure.DELEGATE_FIRST,和上面相反。首先从 delegate 上寻找属性或者方法
  3. Closure.OWNER_ONLY,只在 owner 上寻找,delegate 被忽略。
  4. Closure.DELEGATE_ONLY,和上面相反。只在 delegate 上寻找,owner 被忽略。
  5. Closure.TO_SELF,高级选项,让开发者自定义策略。

示例 1(Closure.OWNER_FIRST):

class Person3 {
    String name
    def pretty = { "My name is $name" }
    String toString() {
        pretty.call()
    }
}

class Thing {
    String name
}

static void main(String[] args) {

    def p = new Person3(name: 'Sarah')
    def t = new Thing(name: 'Teapot')

    assert p.toString() == 'My name is Sarah'
    p.pretty.delegate = t // 默认Closure.OWNER_FIRST,从owner寻找
    assert p.toString() == 'My name is Sarah' // true
}

Person 和 Thing 都定义了 name 属性,使用默认策略,首先会在 Owner 上寻找,所以即使我们把 delegate 换成 t,输出结果还是一样的

示例 2(Closure.DELEGATE_FIRST):

p.pretty.resolveStrategy = Closure.DELEGATE_FIRST
assert p.toString() == 'My name is Teapot'

把委托策略改成 Closure.DELEGATE_FIRST,则会首先去 delegate 寻找 name 属性,如果没有找到,再去 owner 上寻找,但是 delegate 有 name 的定义,所以输出结果为 "My name is Teapot"

示例 3(delegate first vs delegate only):

class Person {
    String name
    int age
    def fetchAge = { age }
}
class Thing {
    String name
}

def p = new Person(name:'Jessica', age:42)
def t = new Thing(name:'Printer')
def cl = p.fetchAge
cl.delegate = p
assert cl() == 42
cl.delegate = t
assert cl() == 42
cl.resolveStrategy = Closure.DELEGATE_ONLY
cl.delegate = p
assert cl() == 42
cl.delegate = t
try {
    cl()
    assert false
} catch (MissingPropertyException ex) {
    // "age" is not defined on the delegate
}

only 时,在对应的对象上找不到时,不会接着去别处找,而是抛出一个异常

测试

在普通类或方法中定义闭包,三者是相同的

class Person {
    def static classClouser = {
        println "classClouser:" + this.hashCode()
        println "classClouser:" + owner.hashCode()
        println "classClouser:" + delegate.hashCode()
    }

    def static method() {
        def classClouser = {
            println "methodclassClouser:" + this.hashCode()
            println "methodclassClouser:" + owner.hashCode()
            println "methodclassClouser:" + delegate.hashCode()
        }
        classClouser.call()
    }
}
public static void main(String[] args) {
    Person.classClouser.call()
    Person.method()
}

输出:

classClouser:1321203216
classClouser:1321203216
classClouser:1321203216
methodclassClouser:1321203216
methodclassClouser:1321203216
methodclassClouser:1321203216
/**
 * 闭包的三个重要变量:this,owner,deleate
 */
//在同一个闭包中,都是相同的对象
//def scriptClouser = {
//    println this//代表闭包定义处的类
//    println owner//代表闭包定义处的类或者对象
//    println delegate//代表任意对象,delegate默认为owner指向的对象
//}
//scriptClouser.call()

//在普通类或方法中定义闭包,三者是相同的
class Person {
//    def static classClouser = {
//        println "classClouser:" + this
//        println "classClouser:" + owner
//        println "classClouser:" + delegate
//    }
//
//    def static method() {
//        def classClouser = {
//            println "methodclassClouser:" + this
//            println "methodclassClouser:" + owner
//            println "methodclassClouser:" + delegate
//        }
//        classClouser.call()
//    }
}
//Person.classClouser.call()
//Person.method()

//闭包内定义闭包    this内部对象    owner和delegate是外部对象
//def nestClouser = {
//    def innerClouser = {
//        println "innerClouser:" + this
//        println "innerClouser:" + owner
//        println "innerClouser:" + delegate
//    }
//    innerClouser.call()
//}
//nestClouser.call()

//修改默认的delegate对象
//Person p=new Person();
//def nestClouser = {
//    def innerClouser = {
//        println "innerClouser:" + this
//        println "innerClouser:" + owner
//        println "innerClouser:" + delegate
//    }
//    innerClouser.delegate=p;
//    innerClouser.call()
//}
//nestClouser.call()

/**
 * 闭包的委托策略
 */
class Student{
    String name
    def pretty={"My name is ${name}"}
    String toString(){
        pretty.call()
    }
}
def student=new Student(name:'jett')

class Teacher{
    String name
}
def teacher=new Teacher(name:'andy')
student.pretty.delegate=teacher
//闭包的委托策略
student.pretty.resolveStrategy=Closure.DELEGATE_FIRST

println student.toString()

Ref

Groovy 之 MOP(invokeMethod 和 methodMissing 方法)

invokeMethod 和 methodMissing

  1. invokeMethod 方法可以分派所有的方法,包括一个类已经实现了的和未实现的方法;而它实现上面的功能是通过这个类实现 GroovyInterceptable 接口达到的,未实现 GroovyInterceptable 接口时,invokeMethod 和 methodMission 功能一致
  2. methodMissing 方法则只能分派一个类未实现的方法,无论它是否实现了 GroovyInterceptable 接口。

如果我们想让一个方法来管理一个类所有方法的调用,那么我们必须使用 "invokeMethod" 方法;如果我们只想通过一个方法来管理一个类的所有 "missing method",即不能被分派出去的方法,那么使用 "methodMissing" 方法是比较有效的;当然,"invokeMethod" 方法也能实现 "methodMissing" 方法的功能。

测试

  1. invokeMethod 未实现 GroovyInterceptable
class InvokeTestor1 {

    def hello() {
        'invoke hello directly'
    }
    String invokeMethod(String name,Object args) {
        return "invokeMethod unknown method $name(${args.join(',')})"
    }
    static main(args) {
        def it = new InvokeTestor1()
        println it.hello()
        println it.foo("mark", 19)
    }
}

输出:

invoke hello directly
invokeMethod unknown method foo(mark,19)
  1. invokeMethod 实现 GroovyInterceptable
class InvokeTestor1 implements GroovyInterceptable {
    def hello() {
        'invoke hello directly'
    }
    String invokeMethod(String name, Object args) {
        return "invokeMethod unknown method $name(${args.join(',')})"
    }
    static main(args) {
        def it = new InvokeTestor1()
        println it.hello()
        println it.foo("mark", 19)
    }
}

输出:

invokeMethod unknown method hello()
invokeMethod unknown method foo(mark,19)
  1. invokeMethod 实现 GroovyInterceptable 和为实现 GroovyInterceptable
class InvokeTestor1 implements GroovyInterceptable {
    def hello() {
        'invoke hello directly'
    }
    def methodMissing(String name, args) {
        return "methodMissing unknown method $name(${args.join(',')})"
    }
    static main(args) {
        def it = new InvokeTestor1()
        println it.hello()
        println it.foo("mark", 19, 20, 30)
    }
}

输出:

invoke hello directly
methodMissing unknown method foo(mark,19,20,30)